﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using XcoAppSpaces.Transport.Sockets.Auth;
using XcoAppSpaces.Transport.Sockets.Auth.Contract;
using System.Threading;
using System.Net.Sockets;
using System.Threading.Tasks;

namespace test.XcoAppSpaces.Transport.Sockets.Auth
{
	[TestFixture]
	public class ReceiveGreetingTests
	{
		private ReceiveGreeting sut;

		[SetUp]
		public void SetUp()
		{
			sut = new ReceiveGreeting();
			sut.Inject(new SecuritySettings { Greeting = "test", SecondsAfterInvalidConnectionIsClosed = 3 });
		}

		[Test]
		public void NoGreetingReceivedInTime_ConnectionInvalid()
		{
			TestGreeting((string)null, false);
		}

		[Test]
		public void IncompleteGreetingReceived_ConnectionInvalid()
		{
			TestGreeting("tes", false);
		}

		[Test]
		public void InvalidGreetingReceived_ConnectionInvalid()
		{
			TestGreeting("1234", false);
		}

		[Test]
		public void CorrectGreetingReceived_ConnectionValid()
		{
			TestGreeting("test", true);
		}

		[Test]
		public void FragmentedGreetingReceived_ConnectionValid()
		{
			TestGreeting(stream =>
			{
				Task.Factory.StartNew(() =>
					{
						var bytes = Encoding.UTF8.GetBytes("test");
						foreach (var b in bytes)
						{
							stream.WriteByte(b);
							Thread.Sleep(500);
						}
					});
			}, true);
		}

		private void TestGreeting(string greetingToWrite, bool shouldBeValid)
		{
			TestGreeting(stream =>
			{
				if (greetingToWrite != null)
				{
					var bytes = Encoding.UTF8.GetBytes(greetingToWrite);
					stream.Write(bytes, 0, bytes.Length);
				}
			}, shouldBeValid);
		}

		private void TestGreeting(Action<NetworkStream> writeGreeting, bool shouldBeValid)
		{
			bool connectionIsInvalid = false;
			bool connectionIsValid = false;
			sut.Out_ValidConnection += _ => connectionIsValid = true;
			sut.Out_InvalidConnection += _ => connectionIsInvalid = true;

			using (var connections = new ConnectedTcpClientPair())
			{
				writeGreeting(connections.SendingClient.GetStream());

				var connInfo = connections.ReceivingClient.GetConnectionInfo();
				connInfo.Stream.ReadTimeout = 3;

				sut.In_Connection(connInfo);

				SpinWait.SpinUntil(() => connectionIsInvalid || connectionIsValid, 4000);

				if (shouldBeValid)
					Assert.IsTrue(connectionIsValid, "No event for valid connection has been received");
				else
					Assert.IsTrue(connectionIsInvalid, "No event for invalid connection has been received");
			}
		}
	}
}
